R est un excellent outil pour représenter des données complexes. On peut faire des choses très belles simplement avec R. La plupart des graphiques produit dans les publications scientifiques de qualité le sont avec R (ou Python). Pour autant, les graphes produits par défaut par R peuvent être très très sales, comme le sont ceux produits par Excel ou autre maléfice du genre.
L’idée du cours d’aujourd’hui est d’apprendre à utiliser le package ggplot2. Ce package implémente la Grammaire des Graphiques dans R. Ce concept repose sur l’idée qu’un graphique est constitué d’une correspondance entre une dimension du jeu de donnée et les propriétés esthétiques de formes géométriques.
C’est une façon de penser les graphiques qui permet de construire assez facilement des graphiques complexes à partir d’idées simples :
Ce cours s’appuie fortement sur ce post de blog.
library(tidyverse)On va créer un jeu de donnée fictif, qui contient trois variables, foo, bar et baz.
df <- data_frame(
foo = c(-122.419416,-121.886329,-71.05888,-74.005941,-118.243685,-117.161084,-0.127758,-77.036871,116.407395,-122.332071,-87.629798,-79.383184,-97.743061,121.473701,72.877656,2.352222,77.594563,-75.165222,-112.074037,37.6173),
bar = c( 37.77493,37.338208,42.360083,40.712784,34.052234,32.715738,51.507351,38.907192,39.904211,47.60621,41.878114,43.653226,30.267153,31.230416,19.075984,48.856614,12.971599,39.952584,33.448377,55.755826),
baz = c(6471,4175,3144,2106,1450,1410,842,835,758,727,688,628,626,510,497,449,419,413,325,318)
)Le but du jeu est de comprendre les relations qui existent entre ces trois variables. C’est souvent ça quand on découvre un nouveau jeu de donnée. Certaines variables sont complètement inconnues, et on essaye de comprendre en quoi elles sont liées aux autres.
(df)## # A tibble: 20 × 3
## foo bar baz
## <dbl> <dbl> <dbl>
## 1 -122.419416 37.77493 6471
## 2 -121.886329 37.33821 4175
## 3 -71.058880 42.36008 3144
## 4 -74.005941 40.71278 2106
## 5 -118.243685 34.05223 1450
## 6 -117.161084 32.71574 1410
## 7 -0.127758 51.50735 842
## 8 -77.036871 38.90719 835
## 9 116.407395 39.90421 758
## 10 -122.332071 47.60621 727
## 11 -87.629798 41.87811 688
## 12 -79.383184 43.65323 628
## 13 -97.743061 30.26715 626
## 14 121.473701 31.23042 510
## 15 72.877656 19.07598 497
## 16 2.352222 48.85661 449
## 17 77.594563 12.97160 419
## 18 -75.165222 39.95258 413
## 19 -112.074037 33.44838 325
## 20 37.617300 55.75583 318
Quand on regarde uniquement les chiffres, on ne comprend pas grand chose. D’où l’intérêt de la visualisation et de la représentation graphique.
df %>% # on se souvient de cet opérateur ?
ggplot(mapping = aes(x = foo, y = bar)) +
geom_point()Voilà la syntaxe d’un scatterplot, un nuage de point, avec ggplot2. Ça n’a pas l’air très sorcier, mais le mécanisme sous-jacent est plutôt complexe. Lorsqu’on en comprend le principe, on pense les représentations graphiques différemment (et on devient bien meilleur avec ggplot2 ;) ).
Quand on a créé ce graphique, on a attribué des dimensions du jeu de données à des attributs esthétiques.
Super.
Les “points” dans le nuage de point sont des objets géométriques qu’on dessine. Ce sont donc des geom_ selon ggplot2. Plus spécifiquement, ce sont des geom_point, une classe d’objet géométrique. Il en existe beaucoup plus, des rectangles, des barres, des lettres.
df %>%
ggplot(mapping = aes(x = foo, y = bar)) +
geom_text(mapping = aes(label = baz))Tous les objets géométriques ont des attributs esthétiques (qui varient selon l’objet). Souvent, ils ont au moins :
Dans l’exemple précédent, l’objet géométrique text requiert un attribut esthétique : l’étiquette ou label. Autrement dit le texte représenté.
Quand on crée un graphique avec ggplot2, en fait on crée une représentation entre les dimensions du jeu de donnée et les attributs aesthétiques des objets géométriques dans notre graphique.
Bon on peut aussi faire ça dans Excel. Mais pas si facilement que ça. Rien que le nuage de point précédent, avec du texte à la place des points, pas si facile dans Excel ?
En fait, en théorie, les objets géométriques n’ont pas uniquement leurs coordonnées x et y comme attributs esthétiques. Donc si on peut attribuer des dimensions à leurs attributs x et y, on devrait pouvoir attribuer d’autres dimensions à d’autres attributs non ?
Si.
Dans ggplot2, le nombre de variables qu’on peut attribuer à des attributs n’est pas limité à x et y. On commence à voir en quoi c’est puissant ?
Créons donc un diagramme en bulle (?), un bubble chart. Comment ? En créant une représentation de la variable baz par la taille de l’objet géométrique point.
df %>%
ggplot(mapping = aes(x = foo, y = bar)) +
geom_point(mapping = aes(size = baz))Donc il vient de se passer ça :
On vient d’attribuer la variable baz à un autre attribut esthétique de l’objet géométrique point, la taille du point.
C’est simple, mais très important. N’importe quel graphique peut être décomposé en spécifications d’objets géométriques et d’attributions de représentations entre les variables du jeu de données et les attributs esthétiques desdits objets.
Quand on comprend ça, on comprend comment construire des représentations complexes.
ggplot2 permet également de construire ses graphiques couches (layer) après couches. C’est un principe important, qui permet de représenter dans un même graphique :
Pour comprendre ce que ça implique, on va modifier le bubble chart :
## MOAR DATA, I NEED MOAR DATAAAA
## install.packages("maps")
library(maps)
(df_more_data <- as_tibble(map_data("world")))## # A tibble: 99,338 × 6
## long lat group order region subregion
## * <dbl> <dbl> <dbl> <int> <chr> <chr>
## 1 -69.89912 12.45200 1 1 Aruba <NA>
## 2 -69.89571 12.42300 1 2 Aruba <NA>
## 3 -69.94219 12.43853 1 3 Aruba <NA>
## 4 -70.00415 12.50049 1 4 Aruba <NA>
## 5 -70.06612 12.54697 1 5 Aruba <NA>
## 6 -70.05088 12.59707 1 6 Aruba <NA>
## 7 -70.03511 12.61411 1 7 Aruba <NA>
## 8 -69.97314 12.56763 1 8 Aruba <NA>
## 9 -69.91181 12.48047 1 9 Aruba <NA>
## 10 -69.89912 12.45200 1 10 Aruba <NA>
## # ... with 99,328 more rows
df %>%
ggplot(aes(x = foo, y = bar)) +
geom_polygon(data = df_more_data, aes(x = long, y = lat, group = group)) +
geom_point(aes(size = baz), color = "red")On construit souvent des graphiques en modifiant progressivement les éléments qui nous intéressent. Pour ça, c’est bien pratique de sauvegarder le graphique dans un objet. On va l’appeler p.
df %>%
ggplot(aes(x = foo, y = bar)) +
geom_polygon(data = df_more_data, aes(x = long, y = lat, group = group)) +
geom_point(aes(size = baz), color = "red")Il semblerait que des points se superposent. On perd du coup de l’information. On va donc changer leur niveau de transparence global. C’est différent d’attribuer des dimensions du jeu de données à des attributs esthétiques, puisque ça se passe en dehors de l’argument mapping =.
Pour modifier le niveau de transparence d’un objet géométrique — les points par exemple, on utilise l’argument alpha. (C’est aussi un attribut esthétique qui peut servir à représenter des données).
df %>%
ggplot(aes(x = foo, y = bar)) +
geom_polygon(data = df_more_data, aes(x = long, y = lat, group = group)) +
geom_point(aes(size = baz), color = "red", alpha = 0.3)Là on voit déjà mieux les points qui se superposent. Cependant, les points sont un poil petits, il faut qu’on modifie l’échelle de taille. On veut que les points représentant une valeur élevée de baz soient plus gros que ceux représentant une valeur faible.
Pour modifier les échelles dans ggplot2, on utilise les fonctions scale_*. Il en existe autant que d’attributs esthétiques.
df %>%
ggplot(aes(x = foo, y = bar)) +
geom_polygon(data = df_more_data, aes(x = long, y = lat, group = group)) +
geom_point(aes(size = baz), color = "red", alpha = 0.3) +
scale_size_continuous( # l'échelle de taille est continue
# sa portée est de 1 à 20 (les gros # points sont 20 fois plus gros que les
# petits) :
range = c(1, 20),
# les cassures qu'on veut représenter dans la légende :
breaks = c(500, 2000, 6000),
# le nom de l'échelle (autrement dit le nom de la variable attribuée) :
name = "Investissements risqués\n(en millions d'USD)\n"
)Bon la carte commence à ressembler à quelque chose. Maintenant on peut jouer sur différents paramètres pour que le graphique soit un peu plus joli.
La grande majorité des petits “trucs” de décoration dans ggplot2 passent par des appels à la fonction theme().
Elle a beaucoup d’arguments, on va jouer avec quelques uns d’entre eux.
df %>%
ggplot(aes(x = foo, y = bar)) +
geom_polygon(data = df_more_data, aes(x = long, y = lat, group = group)) +
geom_point(aes(size = baz), color = "red", alpha = 0.3) +
scale_size_continuous(
range = c(1, 20),
breaks = c(500, 2000, 6000),
name = "Investissements risqués\n(en millions d'USD)\n"
) +
theme(
text = element_text(color = "white"),
panel.background = element_rect(fill = "black"),
panel.grid = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
panel.grid.major = element_blank(),
legend.position = c(0.17, 0.3),
legend.background = element_blank(),
legend.key = element_blank()
)Super, on a fait une jolie carte dans R, avec ggplot2. On a appris pleins de trucs sur le fonctionnement de ggplot2. On a surtout appris à raisonner à propos d’une représentation graphique, et ça c’est super important !
L’idée de la suite est de faire pleins de petits graphiques qu’on fait couramment sur Excel ou dans la vie (à la boulangerie, quand on fait ses courses ou son ménage, dans les transports, tout ça tout ça.)
library(babynames)Un diagramme en barre ne demande qu’une seule attribution à l’attribut esthétique x. En fait on représente en y le nombre de fois que la variable attribuée à x prend une valeur dans un intervalle donné.
babynames %>%
ggplot(aes(x = n)) +
geom_histogram()Super on voit bien rien.
La grande majorité des valeurs que prend n est autour de 0. Ce serait bien de représenter les valeurs de comptage en y sur une échelle logarithmique, pour — attention nouveau mot : — désécraser les fortes valeurs de n.
On l’a dit, tout ce qui est question d’échelle passe par des fonctions qui commencent par scale_.
babynames %>%
ggplot(aes(x = n)) +
geom_histogram() +
scale_y_log10()Un histogramme n’est pas fondamentalement la même chose qu’un diagramme en barre.
babynames %>%
ggplot(aes(x = n)) +
geom_bar() +
scale_y_log10()Vu ? Pas de binning, c’est à dire que les comptages se font valeur par valeur. Ça correspond à représenter ça graphiquement :
head(table(babynames$n), n = 20)##
## 5 6 7 8 9 10 11 12 13 14
## 254615 181502 137015 108297 87470 73004 61401 52890 45772 40039
## 15 16 17 18 19 20 21 22 23 24
## 35654 32340 28717 26051 23603 21685 19614 18366 16813 15709
On peut construire des “camemberts” dans ggplot2, même si ça n’est pas très conseillé.
Pour ça, il faut comprendre qu’on camembert n’est qu’un diagramme en barre, représenté en coordonnées polaires.
Pour représenter le sex ratio d’un échantillon de 100 lignes dans le jeu de donnée :
babynames %>%
# échantillone 100 lignes aléatoirement
sample_n(100) %>%
ggplot(aes(
# ne représente rien en x, juste 1
x = 1,
# et représente par la couleur de la barre le nombre de cas d'un sexe donné
fill = sex)) +
geom_bar()Bon chouette.
Ça c’est en coordonnées cartésiennes comme on a bien l’habitude. Mais si on projette dans un plan polaire, on a quelque chose d’intéressant :
babynames %>%
# échantillone 100 lignes aléatoirement
sample_n(100) %>%
ggplot(aes(
# ne représente rien en x, juste 1
x = 1,
# et représente par la couleur de la barre le nombre de cas d'un sexe donné
fill = sex)) +
geom_bar() +
# on attribue l'angle à la variable `y` (l'axe des y en coordonnées cartésiennes.)
coord_polar(theta = "y")Souvent on a besoin de lignes pour représenter des séries.
babynames %>%
# garde uniquement les cas où on a plus de 75000 prénoms par an.
filter(n > 7.5e4) %>%
ggplot(aes(x = year, y = n, color = name)) +
geom_line()